#include "webserver.h"

int WebServer::m_SIG_pipe[2];

//WebServer::WebServer() {
//
//}
//
//WebServer::~WebServer() {
//
//}

void WebServer::run() {
    int time_slot = -1;
    if (!m_is_stop) {
        LOG_INFO("========== Http Server is Running. ==========");
    }
    while (!m_is_stop) {
        if (m_timeout > 0) {
            time_slot = m_timers->get_next_tick();
        }

        int event_cnt = m_epoller->wait(time_slot);
        for (int i = 0; i < event_cnt; ++i) {
            int fd = m_epoller->get_event_fd(i);
            uint32_t events = m_epoller->get_events(i);
            if (fd == m_listen_fd) {
                LOG_DEBUG("listen event.")
                process_listen_event();

            } else if (events & ((EPOLLRDHUP | EPOLLHUP | EPOLLERR))){
                LOG_DEBUG("Epoll error.")
                close_client(&m_http_clients[fd]);
            } else if (fd == m_SIG_pipe[0] && (events & EPOLLIN)) {

                if (!process_exit_signal()) {
                    LOG_ERROR("Failed to process signal.");
                }
            } else if (events & EPOLLIN) {
                LOG_DEBUG("read event.")
                process_read_event(&m_http_clients[fd]);
            } else if (events & EPOLLOUT) {
                LOG_DEBUG("write event.")
                process_write_event(&m_http_clients[fd]);
            } else {
                LOG_ERROR("Unexpected event.");
            }
        }
    }

}


void WebServer::set_trig_mode(uint32_t trig_mode) {
    switch (trig_mode) {
        case 0:
            m_listen_ev = EPOLLRDHUP;
            m_conn_ev = EPOLLONESHOT | EPOLLRDHUP;
            break;
        case 1:
            m_listen_ev = EPOLLRDHUP;
            m_conn_ev = EPOLLONESHOT | EPOLLRDHUP | EPOLLET;
            break;
        case 2:
            m_listen_ev = EPOLLRDHUP | EPOLLET;
            m_conn_ev = EPOLLONESHOT | EPOLLRDHUP;
            break;
        case 3:
            m_listen_ev = EPOLLRDHUP | EPOLLET;
            m_conn_ev = EPOLLONESHOT | EPOLLRDHUP | EPOLLET;
            break;
        default:
            m_listen_ev = EPOLLRDHUP | EPOLLET;
            m_conn_ev = EPOLLONESHOT | EPOLLRDHUP | EPOLLET;
    }

    HttpConnection::is_ET_mode = (m_conn_ev & EPOLLET);
}

bool WebServer::init_socket() {
    // 1024以下端口号为系统保留端口
    if (m_port > 65535 || m_port < 1024) {
        LOG_ERROR("Unavailable server port: %d", m_port);
        return false;
    }

    // 创建监听socket
    m_listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (m_listen_fd < 0) {
        LOG_ERROR("listen socket error.");
        return false;
    }

    int ret;
    // 优雅关闭：等待缓冲区数据发送完毕后关闭
    struct linger opt_linger{0, 0};
    if (m_linger) {
        opt_linger.l_linger = 1;
        opt_linger.l_onoff = 1;
    }
    ret = setsockopt(m_listen_fd, SOL_SOCKET, SO_LINGER, &opt_linger, sizeof(opt_linger));
    if (ret < 0) {
        close(m_listen_fd);
        LOG_ERROR("setsockopt error.");
        return false;
    }

    // 地址重用, 允许多个进程共用一个本地地址
    int opt_flag;
    opt_flag = 1;
    setsockopt(m_listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt_flag, sizeof(opt_flag));

    // 端口复用，只有最后一个监听端口的进程可以收数据
    opt_flag = 1;
    setsockopt(m_listen_fd, SOL_SOCKET, SO_REUSEPORT, &opt_flag, sizeof(opt_flag));


    // 地址绑定，在setsockopt之后
    struct sockaddr_in addr{};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(m_port);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
//    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    ret = bind(m_listen_fd, (struct sockaddr *)&addr, sizeof(addr));
    if (ret < 0) {
        close(m_listen_fd);
        LOG_ERROR("bind error.");
        return false;
    }

    // 开启监听
    ret = listen(m_listen_fd, 6);
    if (ret < 0) {
        close(m_listen_fd);
        LOG_ERROR("listen error.");
        return false;
    }

    // 加入epoller
    ret = m_epoller->add_fd(m_listen_fd, m_listen_ev | EPOLLIN);
    if (ret < 0) {
        close(m_listen_fd);
        LOG_ERROR("epoll_add error.");
        return false;
    }

    // 设置为非阻塞
    set_fd_nonblock(m_listen_fd);

    // 设置信号以及信号管道
    std::signal(SIGPIPE, SIG_IGN);
    std::signal(SIGTERM, term_sig_handler);
    ret = socketpair(PF_UNIX, SOCK_STREAM, 0, m_SIG_pipe);
    if (ret < 0) {
        close(m_listen_fd);
        LOG_ERROR("pipe socket error.");
        return false;
    }
    ret = m_epoller->add_fd(m_SIG_pipe[0], EPOLLIN | EPOLLRDHUP);   // 将信号源写段添加到epoll
    if (ret < 0) {
        close(m_listen_fd);
        LOG_ERROR("epoll_add error.");
        return false;
    }
    set_fd_nonblock(m_SIG_pipe[1]);        // 读端设为非阻塞

    LOG_INFO("Server port:%d", m_port);
    return true;
}

void WebServer::term_sig_handler(int sig) {
    int save_errno = errno;
    int msg = sig;
    send(m_SIG_pipe[1], (char *)&msg, 1, 0);
    errno = save_errno;
}

int WebServer::set_fd_nonblock(int fd) {
    assert(fd > 0);
    return fcntl(fd, F_SETFL, fcntl(fd, F_GETFD, 0) | O_NONBLOCK);
}


void WebServer::process_listen_event() {
    struct sockaddr_in client_addr{};
    socklen_t len = sizeof(client_addr);
    do {
        // accept
        int client_fd = accept(m_listen_fd, (struct sockaddr *)&client_addr, &len);
        if (client_fd <= 0) {
            return;
        } else if(HttpConnection::m_clients_cnt >= MAX_CLIENT_FD){
            HttpConnection tmp_client;
            tmp_client.init(client_fd, client_addr);
            sent_error(&tmp_client, "Server busy!");
            LOG_WARN("Client capacity is full.");
            return;
        }

        // 添加客户连接
        m_http_clients[client_fd].init(client_fd, client_addr);
        if (m_timeout > 0) {
            // 添加定时器
            m_timers->add(client_fd, m_timeout,
                          [this, client_fd](){
                              LOG_DEBUG("Timer task: close client.")
                             close_client(&m_http_clients[client_fd]);
            });
        }

        // 放入epoll监听
        m_epoller->add_fd(client_fd, m_conn_ev | EPOLLIN);
        set_fd_nonblock(client_fd);

    } while (m_listen_ev & EPOLLET);

}

void WebServer::read_task(HttpConnection *client) {
    assert(client);
    if (!client->read_data()){
        // 读失败则关闭连接
        LOG_DEBUG("Read task failed.")
        close_client(client);
        return;
    }
    LOG_DEBUG("Read task finished.")
    http_process(client);
}

void WebServer::http_process(HttpConnection *client) {
    assert(client);
    if (client->process()) {
        LOG_DEBUG("client->process()")
        m_epoller->mod_fd(client->get_fd(), m_conn_ev | EPOLLOUT);   // 请求处理成功，等待可写事件
    } else {
        m_epoller->mod_fd(client->get_fd(), m_conn_ev | EPOLLIN);   // 请求不完整，等待可读事件
    }
}

void WebServer::write_task(HttpConnection *client) {
    assert(client);
    int write_error;
    if (client->write_data(&write_error)) {
        if (write_error == EAGAIN) {
            // 内核写空间不足
            m_epoller->mod_fd(client->get_fd(), m_conn_ev | EPOLLOUT);
            return;
        }
        if (client->is_keepalive()) {
            // http连接保活
            LOG_DEBUG("Http keepalive.")
            client->reset();
            http_process(client);
            return;
        }
    }

    // http连接不保活或写失败则关闭连接
    LOG_DEBUG("Http non-keepalive.")
    close_client(client);
}

void WebServer::process_read_event(HttpConnection *client) {
    assert(client);
    extend_time(client);
    m_threadpool->add_task([this, client] { read_task(client); });
}

void WebServer::process_write_event(HttpConnection *client) {
    assert(client);
    extend_time(client);
    m_threadpool->add_task([this, client] { write_task(client); });
}

bool WebServer::process_exit_signal() {
    char signal_buf[16];
    long ret = recv(m_SIG_pipe[0], signal_buf, sizeof(signal_buf), 0);
    if (ret <= 0) {
        return false;
    }

    for (int i = 0; i < ret; ++i) {
        if ((int)signal_buf[i] == SIGTERM) {
            m_is_stop = true;
            return true;
        }
    }
    return true;
}

void WebServer::extend_time(HttpConnection *client) {
    if (m_timeout > 0) {
        m_timers->adjust(client->get_fd(), m_timeout);
    }
}

void WebServer::sent_error(HttpConnection *client, const char *info) {
    long ret = send(client->get_fd(), info, sizeof(info), 0);
    if (ret < 0){
        LOG_WARN("sent error to client[%s:%d]", client->get_ip(), client->get_port());
    }
    close(client->get_fd());
}

void WebServer::close_client(HttpConnection *client) {
    assert(client);
    LOG_INFO("Client[%s:%d] closed.", client->get_ip(), client->get_port());
    m_epoller->del_fd(client->get_fd());
    m_timers->erase(client->get_fd());
    client->close_conn();
}










